﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml;
using System.IO;
using System.Diagnostics;
using System.IO.Compression;

using Autodesk.Revit;
using Autodesk.Revit.DB;

using RevitApplication = Autodesk.Revit.ApplicationServices.Application;
using GeometryElement = Autodesk.Revit.DB.GeometryElement;
using GeometryOptions = Autodesk.Revit.DB.Options;
using GeometryInstance = Autodesk.Revit.DB.GeometryInstance;
using RevitView = Autodesk.Revit.DB.View;

namespace BIM.XML3DExport
{
    class XML3DGenerator
    {
        public enum GeneratorStatus { SUCCESS, FAILURE, CANCEL };

        private readonly RevitApplication m_RevitApp;
        public Document m_ActiveDocument;
        private readonly RevitView m_ActiveView;
        private GeometryOptions m_ViewOptions;
        private SortedDictionary<string, Category> m_Categories;
        private Settings m_Settings;

        public XYZ viewPosition = null;
        public XYZ viewDirection = null;
        public XYZ viewUpDir = null;
        public XYZ viewRightDir = null;

        public List<String> facesListString = new List<String>();
        public List<String> vertexListString = new List<String>();
        public List<String> normalListString = new List<String>();
        public List<String> polylineListString = new List<String>();
        public List<Color> faceColorList = new List<Color>();
        public List<double> faceTransList = new List<double>();
        public double dR = 0.0;
        public double dG = 0.0;
        public double dB = 0.0;
        public double dA = 0.0;

        public String elementGUID = null;
        public String useParameter = null;

        private int nodeID = 0;

        //Test function for create sample 3dxml file, just testing
        public GeneratorStatus Create3DXMLFile(string fileName)
        {
            nodeID = 0;
            string AddInPath = BIM.XML3DExport.XML3DExportUI.AddInPath;
            string directoryName = Path.GetDirectoryName(AddInPath);
            string foldPath = directoryName + @"\3DXML";
            if (!Directory.Exists(foldPath))
            {
                Directory.CreateDirectory(foldPath);
            }
            //Step1: Manifest file
            GenerateManifestFile(foldPath + @"\Manifest.xml");
            //Setp2: Model structure file WH.3dxml//TODO
            XmlElement strNode;
            XmlDocument strDoc = GenerateWH3DXMLFile(out strNode);
            int id1 = AddFamilyNodeToStructure("柱", strDoc, strNode);
            int id2 = AddFamilyNodeToStructure("门", strDoc, strNode);
            int id3 = AddFamilyNodeToStructure("窗", strDoc, strNode);

            AddModelNodeToStructure("ZZ_1", strDoc, strNode, id1, "WH_1.3DRep");
            AddModelNodeToStructure("M_1", strDoc, strNode, id2, "WH_2.3DRep");
            AddModelNodeToStructure("C_1", strDoc, strNode, id3, "WH_3.3DRep");
            if (strDoc != null)
            {
                strDoc.Save(foldPath + @"\WH.3dxml");
            }
            //Step3: 3DRep file for geometry WH_**.3DRep//TODO
            //Move to get element mesh info
            //Generate3DRepFile(foldPath + @"\WH_1.3DRep");
            //Step4: Create zip (3dxml) file
            GeneratorStatus suc = CreateZipBy7Zip(foldPath, fileName);
            //Step5: Delete all the file on "foldPath"
            DirectoryInfo di = new DirectoryInfo(foldPath);
            di.Delete(true);
            return suc;
        }

        //Add 3drep file name to structure
        public int AddModelNodeToStructure(String modelNameStr,
            XmlDocument doc, XmlElement strNode, int familyID,
            String fileName)
        {
            nodeID++;
            XmlElement Instance3D = doc.CreateElement("Instance3D");
            strNode.AppendChild(Instance3D);
            XmlAttribute Rootxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            Rootxsitype.Value = "Instance3DType";
            XmlAttribute instanceID = doc.CreateAttribute("id");
            instanceID.Value = nodeID++.ToString();
            Instance3D.Attributes.Append(Rootxsitype);
            Instance3D.Attributes.Append(instanceID);

            XmlElement IsAggregatedBy = doc.CreateElement("IsAggregatedBy");
            IsAggregatedBy.InnerText = familyID.ToString();//上层family节点
            XmlElement IsInstanceOf = doc.CreateElement("IsInstanceOf");
            IsInstanceOf.InnerText = nodeID++.ToString();
            XmlElement RelativeMatrix = doc.CreateElement("RelativeMatrix");
            RelativeMatrix.InnerText = "1 0 0 0 1 0 0 0 1 0 0 0";
            //Add by jsh for Composer attribute
            XmlElement UUID = doc.CreateElement("UUID");
            UUID.InnerText = elementGUID;

            XmlElement userParameterNode = doc.CreateElement("UserParameters");
            userParameterNode.InnerText = useParameter;

            Instance3D.AppendChild(IsAggregatedBy);
            Instance3D.AppendChild(IsInstanceOf);
            Instance3D.AppendChild(RelativeMatrix);
            Instance3D.AppendChild(UUID);
            Instance3D.AppendChild(userParameterNode);

            XmlElement Reference3D = doc.CreateElement("Reference3D");
            strNode.AppendChild(Reference3D);
            XmlAttribute Reference3Dxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            Reference3Dxsitype.Value = "Reference3DType";
            XmlAttribute Reference3DID = doc.CreateAttribute("id");
            Reference3DID.Value = (--nodeID).ToString();
            XmlAttribute Reference3DName = doc.CreateAttribute("name");
            Reference3DName.Value = modelNameStr;
            Reference3D.Attributes.Append(Reference3Dxsitype);
            Reference3D.Attributes.Append(Reference3DID);
            Reference3D.Attributes.Append(Reference3DName);
            int modelNode = nodeID;

            //////////////////////////rep/////////////////////////////////
            nodeID++;
            XmlElement InstanceRep = doc.CreateElement("InstanceRep");
            strNode.AppendChild(InstanceRep);
            XmlAttribute Repxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            Repxsitype.Value = "InstanceRepType";
            XmlAttribute instanceRepID = doc.CreateAttribute("id");
            instanceRepID.Value = nodeID++.ToString();
            InstanceRep.Attributes.Append(Repxsitype);
            InstanceRep.Attributes.Append(instanceRepID);

            XmlElement RepIsAggregatedBy = doc.CreateElement("IsAggregatedBy");
            RepIsAggregatedBy.InnerText = modelNode.ToString();//上层模型节点
            XmlElement RepIsInstanceOf = doc.CreateElement("IsInstanceOf");
            RepIsInstanceOf.InnerText = nodeID++.ToString();
            InstanceRep.AppendChild(RepIsAggregatedBy);
            InstanceRep.AppendChild(RepIsInstanceOf);

            XmlElement ReferenceRep = doc.CreateElement("ReferenceRep");
            strNode.AppendChild(ReferenceRep);
            XmlAttribute ReferenceRepxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            ReferenceRepxsitype.Value = "ReferenceRepType";
            XmlAttribute ReferenceRepID = doc.CreateAttribute("id");
            ReferenceRepID.Value = (--nodeID).ToString();
            XmlAttribute ReferenceFormat = doc.CreateAttribute("format");
            ReferenceFormat.Value = "TESSELLATED";
            XmlAttribute ReferenceVersion = doc.CreateAttribute("version");
            ReferenceVersion.Value = "1.2";
            XmlAttribute ReferenceFileName = doc.CreateAttribute("associatedFile");
            ReferenceFileName.Value = "urn:3DXML:" + fileName;
            ReferenceRep.Attributes.Append(ReferenceRepxsitype);
            ReferenceRep.Attributes.Append(ReferenceRepID);
            ReferenceRep.Attributes.Append(ReferenceFormat);
            ReferenceRep.Attributes.Append(ReferenceVersion);
            ReferenceRep.Attributes.Append(ReferenceFileName);

            XmlElement externalID = doc.CreateElement("PLM_ExternalID");
            externalID.InnerText = elementGUID;
            ReferenceRep.AppendChild(externalID);

            return nodeID;
        }

        public int AddFamilyNodeToStructure(String familyNameStr,
            XmlDocument doc, XmlElement strNode)
        {
            nodeID++;
            XmlElement Instance3D = doc.CreateElement("Instance3D");
            strNode.AppendChild(Instance3D);
            XmlAttribute Rootxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            Rootxsitype.Value = "Instance3DType";
            XmlAttribute instanceID = doc.CreateAttribute("id");
            instanceID.Value = nodeID++.ToString();
            Instance3D.Attributes.Append(Rootxsitype);
            Instance3D.Attributes.Append(instanceID);

            XmlElement IsAggregatedBy = doc.CreateElement("IsAggregatedBy");
            IsAggregatedBy.InnerText = "1";//最上层节点
            XmlElement IsInstanceOf = doc.CreateElement("IsInstanceOf");
            IsInstanceOf.InnerText = nodeID++.ToString();
            XmlElement RelativeMatrix = doc.CreateElement("RelativeMatrix");
            RelativeMatrix.InnerText = "1 0 0 0 1 0 0 0 1 0 0 0";
            Instance3D.AppendChild(IsAggregatedBy);
            Instance3D.AppendChild(IsInstanceOf);
            Instance3D.AppendChild(RelativeMatrix);

            XmlElement Reference3D = doc.CreateElement("Reference3D");
            strNode.AppendChild(Reference3D);
            XmlAttribute Reference3Dxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
            Reference3Dxsitype.Value = "Reference3DType";
            XmlAttribute Reference3DID = doc.CreateAttribute("id");
            Reference3DID.Value = (--nodeID).ToString();
            XmlAttribute Reference3DName = doc.CreateAttribute("name");
            Reference3DName.Value = familyNameStr;
            Reference3D.Attributes.Append(Reference3Dxsitype);
            Reference3D.Attributes.Append(Reference3DID);
            Reference3D.Attributes.Append(Reference3DName);

            return nodeID;
        }

        //Use the new zip method, for CATIA V5 read 3dxml file
        public GeneratorStatus CreateZipBy7Zip(string filesPath, string zipFilePath)
        {
            GeneratorStatus suc = GeneratorStatus.SUCCESS;

            try
            {
                string[] filenames = Directory.GetFiles(filesPath);
                using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
                {
                    foreach (string path in filenames)
                    {
                        if (File.Exists(path))//如果是文件
                        {
                            zip.AddFile(path, "");
                        }
                    }
                    zip.Save(zipFilePath);
                }
            }
            catch (Exception ex)
            {
                string errorMsg = ex.Message;
                suc = GeneratorStatus.FAILURE;
            }
            return suc;
        }

        //Use the zip method, V5 can't read 3dxml file
        //public GeneratorStatus CreateZipFile(string filesPath, string zipFilePath)
        //{
        //    GeneratorStatus suc = GeneratorStatus.FAILURE;
        //    if (!Directory.Exists(filesPath))
        //    {
        //        Console.WriteLine("Cannot find directory '{0}'", filesPath);
        //        return suc;
        //    }

        //    try
        //    {
        //        string[] filenames = Directory.GetFiles(filesPath);
        //        using (ZipOutputStream s = new ZipOutputStream(File.Create(zipFilePath)))
        //        {
        //            s.SetLevel(9); // 压缩级别 0-9
        //            byte[] buffer = new byte[14096]; //缓冲区大小
        //            foreach (string file in filenames)
        //            {
        //                ZipEntry entry = new ZipEntry(Path.GetFileName(file));
        //                entry.DateTime = DateTime.Now;
        //                s.PutNextEntry(entry);
        //                using (FileStream fs = File.OpenRead(file))
        //                {
        //                    int sourceBytes;
        //                    do
        //                    {
        //                        sourceBytes = fs.Read(buffer, 0, buffer.Length);
        //                        s.Write(buffer, 0, sourceBytes);
        //                    } while (sourceBytes > 0);
        //                }
        //            }
        //            s.Finish();
        //            s.Close();
        //        }
        //        suc = GeneratorStatus.SUCCESS;
        //    }
        //    catch (Exception ex)
        //    {
        //        Console.WriteLine("Exception during processing {0}", ex);
        //        suc = GeneratorStatus.FAILURE;
        //    }
        //    return suc;
        //}

        public void Generate3DRepFile(string strPath)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null));
                XmlElement XMLRepresentation = doc.CreateElement("XMLRepresentation");
                doc.AppendChild(XMLRepresentation);
                XmlAttribute version = doc.CreateAttribute("version");
                version.Value = "1.2";
                XmlAttribute xlns = doc.CreateAttribute("xmlns");
                xlns.Value = "http://www.3ds.com/xsd/3DXML";
                XmlAttribute xsi = doc.CreateAttribute("xmlns:xsi");
                xsi.Value = "http://www.w3.org/2001/XMLSchema-instance";
                XMLRepresentation.Attributes.Append(version);
                XMLRepresentation.Attributes.Append(xlns);
                XMLRepresentation.Attributes.Append(xsi);

                XmlElement Root = doc.CreateElement("Root");
                XMLRepresentation.AppendChild(Root);
                XmlAttribute Rootxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                Rootxsitype.Value = "BagRepType";
                Root.Attributes.Append(Rootxsitype);

                bool bUseFaceColor = false;
                int faceNum = facesListString.Count;
                int polyNum = polylineListString.Count;
                int faceColorNum = faceColorList.Count;
                int transNum = faceTransList.Count;
                if (faceNum == faceColorNum && faceNum == transNum)
                {
                    bUseFaceColor = true;
                }
                if (faceNum == 0 && polyNum == 0)
                {
                    return;
                }

                for (int i = 0; i < faceNum; ++i)
                {
                    XmlElement Rep = doc.CreateElement("Rep");
                    Root.AppendChild(Rep);
                    XmlAttribute Repxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                    Repxsitype.Value = "PolygonalRepType";
                    XmlAttribute RepId = doc.CreateAttribute("id");
                    RepId.Value = (i + 1).ToString();
                    XmlAttribute Repaccuracy = doc.CreateAttribute("accuracy");
                    Repaccuracy.Value = "0";
                    XmlAttribute Repsolid = doc.CreateAttribute("solid");
                    Repsolid.Value = "1";
                    Rep.Attributes.Append(Repxsitype);
                    Rep.Attributes.Append(RepId);
                    Rep.Attributes.Append(Repaccuracy);
                    Rep.Attributes.Append(Repsolid);

                    String faceStr = facesListString[i];
                    String vertexStr = vertexListString[i];
                    String normalStr = normalListString[i];

                    XmlElement Faces = doc.CreateElement("Faces");
                    Rep.AppendChild(Faces);
                    XmlElement Face = doc.CreateElement("Face");
                    XmlAttribute triangles = doc.CreateAttribute("triangles");
                    triangles.Value = faceStr;
                    Face.Attributes.Append(triangles);
                    Faces.AppendChild(Face);
                    XmlElement SurfaceAttributes = doc.CreateElement("SurfaceAttributes");
                    Face.AppendChild(SurfaceAttributes);
                    XmlElement Color = doc.CreateElement("Color");
                    SurfaceAttributes.AppendChild(Color);
                    XmlAttribute Colorxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                    Colorxsitype.Value = "RGBAColorType";
                    XmlAttribute Red = doc.CreateAttribute("red");
                    XmlAttribute green = doc.CreateAttribute("green");
                    XmlAttribute blue = doc.CreateAttribute("blue");
                    XmlAttribute alpha = doc.CreateAttribute("alpha");
                    if (bUseFaceColor)
                    {
                        Color curFaceColor = faceColorList[i];
                        Red.Value = (curFaceColor.Red / 255.0).ToString("F6");
                        green.Value = (curFaceColor.Green / 255.0).ToString("F6");
                        blue.Value = (curFaceColor.Blue / 255.0).ToString("F6");
                        alpha.Value = ((100 - faceTransList[i]) / 100.0).ToString();
                    }
                    else
                    {
                        Red.Value = (dR / 255.0).ToString("F6");
                        green.Value = (dG / 255.0).ToString("F6");
                        blue.Value = (dB / 255.0).ToString("F6");
                        alpha.Value = ((100 - dA) / 100.0).ToString();
                    }
                    Color.Attributes.Append(Colorxsitype);
                    Color.Attributes.Append(Red);
                    Color.Attributes.Append(green);
                    Color.Attributes.Append(blue);
                    Color.Attributes.Append(alpha);

                    XmlElement VertexBuffer = doc.CreateElement("VertexBuffer");
                    Rep.AppendChild(VertexBuffer);
                    XmlElement Positions = doc.CreateElement("Positions");
                    Positions.InnerText = vertexStr;
                    VertexBuffer.AppendChild(Positions);
                    XmlElement Normals = doc.CreateElement("Normals");
                    Normals.InnerText = normalStr;
                    VertexBuffer.AppendChild(Normals);
                }

                for (int j = 0; j < polylineListString.Count; ++j)
                {
                    int repID = j + faceNum;
                    XmlElement Rep = doc.CreateElement("Rep");
                    Root.AppendChild(Rep);
                    XmlAttribute Repxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                    Repxsitype.Value = "PolygonalRepType";
                    XmlAttribute RepId = doc.CreateAttribute("id");
                    RepId.Value = repID.ToString();
                    XmlAttribute Repaccuracy = doc.CreateAttribute("accuracy");
                    Repaccuracy.Value = "0.2";
                    XmlAttribute Repsolid = doc.CreateAttribute("solid");
                    Repsolid.Value = "1";
                    Rep.Attributes.Append(Repxsitype);
                    Rep.Attributes.Append(RepId);
                    Rep.Attributes.Append(Repaccuracy);
                    Rep.Attributes.Append(Repsolid);

                    XmlElement Edges = doc.CreateElement("Edges");
                    Rep.AppendChild(Edges);

                    XmlElement LineAttributes = doc.CreateElement("LineAttributes");
                    Edges.AppendChild(LineAttributes);
                    XmlAttribute lineType = doc.CreateAttribute("lineType");
                    lineType.Value = "SOLID";
                    XmlAttribute thickness = doc.CreateAttribute("thickness");
                    thickness.Value = "2";
                    LineAttributes.Attributes.Append(lineType);
                    LineAttributes.Attributes.Append(thickness);
                    XmlElement Color = doc.CreateElement("Color");
                    LineAttributes.AppendChild(Color);
                    XmlAttribute Colorxsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                    Colorxsitype.Value = "RGBAColorType";
                    XmlAttribute Red = doc.CreateAttribute("red");
                    Red.Value = "0";
                    XmlAttribute green = doc.CreateAttribute("green");
                    green.Value = "0";
                    XmlAttribute blue = doc.CreateAttribute("blue");
                    blue.Value = "0";
                    XmlAttribute alpha = doc.CreateAttribute("alpha");
                    alpha.Value = "1";
                    Color.Attributes.Append(Colorxsitype);
                    Color.Attributes.Append(Red);
                    Color.Attributes.Append(green);
                    Color.Attributes.Append(blue);
                    Color.Attributes.Append(alpha);

                    XmlElement Polyline = doc.CreateElement("Polyline");
                    Edges.AppendChild(Polyline);
                    XmlAttribute vertices = doc.CreateAttribute("vertices");
                    vertices.Value = polylineListString[j];
                    Polyline.Attributes.Append(vertices);
                }

                doc.Save(strPath);
            }
            catch (System.Exception ex)
            {
                String error = ex.ToString();
                Console.WriteLine(error);
            }
        }

        public XmlDocument GenerateWH3DXMLFile(out XmlElement strNode)
        {
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null));
                XmlElement Model = doc.CreateElement("Model_3dxml");
                doc.AppendChild(Model);
                XmlAttribute xlns = doc.CreateAttribute("xmlns");
                xlns.Value = "http://www.pengrui.com/3DXML";
                XmlAttribute xsi = doc.CreateAttribute("xmlns:xsi");
                xsi.Value = "http://www.w3.org/2001/XMLSchema-instance";
                XmlAttribute xsispace = doc.CreateAttribute("xmlns:xlink");
                xsispace.Value = "http://www.w3.org/1999/xlink";
                Model.Attributes.Append(xlns);
                Model.Attributes.Append(xsi);
                Model.Attributes.Append(xsispace);

                XmlElement Header = doc.CreateElement("Header");
                Model.AppendChild(Header);
                XmlElement SchemaVersion = doc.CreateElement("SchemaVersion");
                SchemaVersion.InnerText = "4.0";
                Header.AppendChild(SchemaVersion);

                XmlElement ProductStructure = doc.CreateElement("ProductStructure");
                XmlAttribute Psroot = doc.CreateAttribute("root");
                Psroot.Value = "1";
                ProductStructure.Attributes.Append(Psroot);
                Model.AppendChild(ProductStructure);

                XmlElement Reference3D = doc.CreateElement("Reference3D");
                ProductStructure.AppendChild(Reference3D);
                XmlAttribute Reference3Dxsitype = doc.CreateAttribute("xsi", "type", "http://www.w3.org/2001/XMLSchema-instance");
                Reference3Dxsitype.Value = "Reference3DType";
                XmlAttribute Reference3DID = doc.CreateAttribute("id");
                Reference3DID.Value = "1";
                nodeID++;
                XmlAttribute Reference3DName = doc.CreateAttribute("name");
                if (m_ActiveDocument != null)
                {
                    Reference3DName.Value = m_ActiveDocument.Title + "_Revit模型";
                }
                else
                {
                    Reference3DName.Value = "Revit模型";
                }
                Reference3D.Attributes.Append(Reference3Dxsitype);
                Reference3D.Attributes.Append(Reference3DID);
                Reference3D.Attributes.Append(Reference3DName);

                XmlElement DefaultView = doc.CreateElement("DefaultView");
                Model.AppendChild(DefaultView);
                XmlElement Viewpoint = doc.CreateElement("Viewpoint");
                DefaultView.AppendChild(Viewpoint);
                XmlAttribute xsitype = doc.CreateAttribute("xsi:type", "http://www.w3.org/2001/XMLSchema-instance");
                xsitype.Value = "ParallelViewpointType";
                XmlAttribute visualizedHeight = doc.CreateAttribute("visualizedHeight");
                visualizedHeight.Value = "12908.85871";
                XmlAttribute targetDistance = doc.CreateAttribute("targetDistance");
                targetDistance.Value = "53769.26563";
                XmlAttribute nearPlaneDistance = doc.CreateAttribute("nearPlaneDistance");
                nearPlaneDistance.Value = "28129.89063";
                XmlAttribute farPlaneDistance = doc.CreateAttribute("farPlaneDistance");
                farPlaneDistance.Value = "79408.67969";
                Viewpoint.Attributes.Append(xsitype);
                Viewpoint.Attributes.Append(visualizedHeight);
                Viewpoint.Attributes.Append(targetDistance);
                Viewpoint.Attributes.Append(nearPlaneDistance);
                Viewpoint.Attributes.Append(farPlaneDistance);

                XmlElement Position = doc.CreateElement("Position");
                if (viewPosition != null)
                {
                    Position.InnerText = String.Format("{0} {1} {2}",
                       viewPosition.X, viewPosition.Y, viewPosition.Z);
                }
                else
                {
                    Position.InnerText = String.Format("{0} {1} {2}",
                       0.0, 0.0, 0.0);
                }
                XmlElement Sight = doc.CreateElement("Sight");
                if (viewDirection != null)
                {
                    Sight.InnerText = String.Format("{0} {1} {2}",
                        viewDirection.X, viewDirection.Y, viewDirection.Z);
                }
                else
                {
                    Sight.InnerText = String.Format("{0} {1} {2}",
                        0, 1, 0);
                }
                XmlElement Right = doc.CreateElement("Right");
                if (viewRightDir != null)
                {
                    Right.InnerText = String.Format("{0} {1} {2}",
                       viewRightDir.X, viewRightDir.Y, viewRightDir.Z);
                }
                else
                {
                    Right.InnerText = String.Format("{0} {1} {2}",
                       1, 0, 0);
                }

                XmlElement Up = doc.CreateElement("Up");
                if (viewUpDir != null)
                {
                    Up.InnerText = String.Format("{0} {1} {2}",
                        viewUpDir.X, viewUpDir.Y, viewUpDir.Z);
                }
                else
                {
                    Up.InnerText = String.Format("{0} {1} {2}",
                        0, 0, 1);
                }

                Viewpoint.AppendChild(Position);
                Viewpoint.AppendChild(Sight);
                Viewpoint.AppendChild(Right);
                Viewpoint.AppendChild(Up);

                strNode = ProductStructure;
                return doc;
            }
            catch (System.Exception ex)
            {
                String error = ex.ToString();
                Console.WriteLine(error);
                strNode = null;
                return null;
            }
        }

        public void GenerateManifestFile(string strPath)
        {
            try
            {
                //定义一个XDocument结构
                XmlDocument doc = new XmlDocument();
                doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null));
                XmlElement Manifest = doc.CreateElement("Manifest");
                doc.AppendChild(Manifest);
                XmlAttribute xsi = doc.CreateAttribute("xmlns:xsi");
                xsi.Value = "http://www.w3.org/2001/XMLSchema-instance";
                XmlAttribute xsispace = doc.CreateAttribute("xsi:noNamespaceSchemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
                xsispace.Value = "Manifest.xsd";
                Manifest.Attributes.Append(xsi);
                Manifest.Attributes.Append(xsispace);

                XmlElement Root = doc.CreateElement("Root");
                Root.InnerText = "WH.3dxml";
                XmlElement WithAuthoringData = doc.CreateElement("WithAuthoringData");
                WithAuthoringData.InnerText = "false";
                Manifest.AppendChild(Root);
                Manifest.AppendChild(WithAuthoringData);

                doc.Save(strPath);
            }
            catch (System.Exception ex)
            {
                String error = ex.ToString();
                Console.WriteLine(error);
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="revit">
        /// The application object for the active instance of Autodesk Revit.
        /// </param>
        public XML3DGenerator()
        {
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="revit">
        /// The application object for the active instance of Autodesk Revit.
        /// </param>
        public XML3DGenerator(RevitApplication revitApp, Document doc, RevitView view)
        {
            //initialize the member variable
            if (revitApp != null)
            {
                m_RevitApp = revitApp;
                m_ActiveDocument = doc;
                m_ActiveView = view;

                m_ViewOptions = m_RevitApp.Create.NewGeometryOptions();
                m_ViewOptions.View = m_ActiveView;
            }
        }

        /// <summary>
        /// Scans all elements in the active document and creates a list of
        /// the categories of those elements.
        /// </summary>
        /// <returns>Sorted dictionary of categories.</returns>
        public SortedDictionary<string, Category> ScanCategories()
        {
            m_Categories = new SortedDictionary<string, Category>();

            // get all elements in the active document
            FilteredElementCollector filterCollector = new FilteredElementCollector(m_ActiveDocument);

            filterCollector.WhereElementIsNotElementType();

            FilteredElementIterator iterator = filterCollector.GetElementIterator();

            // create sorted dictionary of the categories of the elements
            while (iterator.MoveNext())
            {
                Element element = iterator.Current;

                if (element.Category != null)
                {
                    if (!m_Categories.ContainsKey(element.Category.Name))
                    {
                        m_Categories.Add(element.Category.Name, element.Category);
                    }
                }
            }

            return m_Categories;
        }

        /// <summary>
        /// Gets all categories in all open documents if allCategories is true
        /// or the categories of the elements in the active document if allCategories
        /// is set to false. 
        /// </summary>
        /// <param name="allCategories">True to get all categores in all open documents, 
        /// false to get the categories of the elements in the active document.</param>
        /// <returns>Sorted dictionary of categories.</returns>
        public SortedDictionary<string, Category> ScanCategories(bool allCategories)
        {
            if (!allCategories)
            {
                return ScanCategories();
            }
            else
            {
                // get and return all categories
                SortedDictionary<string, Category> sortedCategories = new SortedDictionary<string, Category>();

                // scan the active document for categories
                foreach (Category category in m_ActiveDocument.Settings.Categories)
                {
                    if (!sortedCategories.ContainsKey(category.Name))
                        sortedCategories.Add(category.Name, category);
                }

                // if linked models exist scan for categories
                List<Document> linkedDocs = GetLinkedModels();

                foreach (Document linkedDoc in linkedDocs)
                {
                    foreach (Category category in linkedDoc.Settings.Categories)
                    {
                        if (!sortedCategories.ContainsKey(category.Name))
                            sortedCategories.Add(category.Name, category);
                    }
                }

                return sortedCategories;
            }
        }

        /// <summary>
        /// Get every Element in all open documents.
        /// </summary>
        /// <param name="exportRange">
        /// The range of elements to be exported.
        /// </param>
        private void ScanElement(ElementsExportRange exportRange)
        {
            List<Document> documents = new List<Document>();

            // active document should be the first document in the list
            documents.Add(m_ActiveDocument);

            // figure out if we need to get linked models
            if (m_Settings.IncludeLinkedModels)
            {
                List<Document> linkedDocList = GetLinkedModels();
                documents.AddRange(linkedDocList);
            }

            foreach (Document doc in documents)
            {
                FilteredElementCollector collector = null;

                if (ElementsExportRange.OnlyVisibleOnes == exportRange)
                {
                    // find the view having the same name of ActiveView.Name in active and linked model documents.
                    ElementId viewId = FindView(doc, m_ActiveView.Name);

                    if (viewId != ElementId.InvalidElementId)
                        collector = new FilteredElementCollector(doc, viewId);
                    else
                        collector = new FilteredElementCollector(doc);
                }
                else
                {
                    collector = new FilteredElementCollector(doc);
                }

                collector.WhereElementIsNotElementType();

                FilteredElementIterator iterator = collector.GetElementIterator();

                while (iterator.MoveNext())
                {
                    System.Windows.Forms.Application.DoEvents();

                    //                     if (m_XML3DExporterCancel.CancelProcess == true)
                    //                         return;

                    Element element = iterator.Current;

                    // check if element's category is in the list, if it is continue.
                    // if there are no selected categories, take anything.
                    if (m_Settings.SelectedCategories.Count > 0)
                    {
                        if (element.Category == null)
                        {
                            continue;
                        }
                        else
                        {
                            IEnumerable<Category> cats = from cat in m_Settings.SelectedCategories
                                                         where cat.Value.Id == element.Category.Id
                                                         select cat.Value;

                            if (cats.Count() == 0)
                            {
                                continue;
                            }
                        }
                    }

                    // get the GeometryElement of the element
                    GeometryElement geometry = null;
                    geometry = element.get_Geometry(m_ViewOptions);

                    if (null == geometry)
                    {
                        continue;
                    }

                    // get the solids in GeometryElement
                    ScanGeomElement(geometry, null);
                }
            }
        }

        /// <summary>
        /// Get view by view name.
        /// </summary>
        /// <param name="doc">The document to find the view.</param>
        /// <param name="activeViewName">The view name.</param>
        /// <returns>The element id of the view found.</returns>
        private ElementId FindView(Document doc, string activeViewName)
        {
            FilteredElementCollector collector = new FilteredElementCollector(doc);
            collector.OfClass(typeof(RevitView));

            IEnumerable<Element> selectedView = from view in collector.ToList<Element>()
                                                where view.Name == activeViewName
                                                select view;

            if (selectedView.Count() > 0)
            {
                return (selectedView.First() as RevitView).Id;
            }

            return ElementId.InvalidElementId;
        }

        /// <summary>
        /// Scan GeometryElement to collect triangles.
        /// </summary>
        /// <param name="geometry">The geometry element.</param>
        /// <param name="trf">The transformation.</param>
        private void ScanGeomElement(GeometryElement geometry, Transform transform)
        {
            //get all geometric primitives contained in the GeometryElement
            foreach (GeometryObject gObject in geometry)
            {
                // if the type of the geometric primitive is Solid
                Solid solid = gObject as Solid;
                if (null != solid)
                {
                    ScanSolid(solid, transform);
                    continue;
                }

                // if the type of the geometric primitive is instance
                GeometryInstance instance = gObject as GeometryInstance;
                if (null != instance)
                {
                    ScanGeometryInstance(instance, transform);
                    continue;
                }

                GeometryElement geomElement = gObject as GeometryElement;
                if (null != geomElement)
                {
                    ScanGeomElement(geomElement, transform);
                }
            }
        }

        /// <summary>
        /// Scan GeometryInstance to collect triangles.
        /// </summary>
        /// <param name="instance">The geometry instance.</param>
        /// <param name="trf">The transformation.</param>
        private void ScanGeometryInstance(GeometryInstance instance, Transform transform)
        {
            GeometryElement instanceGeometry = instance.SymbolGeometry;
            if (null == instanceGeometry)
            {
                return;
            }
            Transform newTransform;
            if (null == transform)
            {
                newTransform = instance.Transform;
            }
            else
            {
                newTransform = transform.Multiply(instance.Transform);	// get a transformation of the affine 3-space
            }

            // get all geometric primitives contained in the GeometryElement
            ScanGeomElement(instanceGeometry, newTransform);
        }

        /// <summary>
        /// Scan Solid to collect triangles.
        /// </summary>
        /// <param name="solid">The solid.</param>
        /// <param name="trf">The transformation.</param>
        private void ScanSolid(Solid solid, Transform transform)
        {
            //    GetTriangular(solid, transform);	// get triangles in the solid
        }

        /// <summary>
        /// Scans and returns the documents linked to the current model.
        /// </summary>
        /// <returns>List of linked documents.</returns>
        private List<Document> GetLinkedModels()
        {
            List<Document> linkedDocs = new List<Document>();

            try
            {
                // scan the current model looking for Revit links
                List<Element> linkedElements = FindLinkedModelElements();

                foreach (Element linkedElem in linkedElements)
                {
                    RevitLinkType linkType = linkedElem as RevitLinkType;

                    if (linkType != null)
                    {
                        // now look that up in the open documents
                        foreach (Document openedDoc in m_RevitApp.Documents)
                        {
                            if (openedDoc.Title.ToUpper() == linkType.Name.ToUpper()) linkedDocs.Add(openedDoc);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                XMLDialogManager.ShowDebug(ex.Message);
            }

            return linkedDocs;

        }

        /// <summary>
        /// Scan model and return linked model elements.
        /// </summary>
        /// <returns>List of linked model elements.</returns>
        private List<Element> FindLinkedModelElements()
        {
            Document doc = m_ActiveDocument;

            FilteredElementCollector linksCollector = new FilteredElementCollector(doc);
            List<Element> linkElements = linksCollector.WherePasses(new ElementCategoryFilter(BuiltInCategory.OST_RvtLinks)).ToList<Element>();

            FilteredElementCollector familySymbolCollector = new FilteredElementCollector(doc);
            linkElements.AddRange(familySymbolCollector.OfClass(typeof(Autodesk.Revit.DB.FamilySymbol)).ToList<Element>());

            return linkElements;
        }
    }
}
